Title Banner


Technotes


File Manager File Handling Q&As



Technote FL 515October 1990



Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990

This Technical Note contains a collection of archived Q&As relating to a specific topic--questions sent the Developer Support Center (DSC) along with answers from the DSC engineers. Current Q&A's can be found on the Macintosh Technical Q&A's web site.


Locking and unlocking a Macintosh file

Date Written: 3/12/92

Last reviewed: 6/14/93

Is there any way to lock or protect a file in such a way that the user cannot unlock it with the Get Info window? I realize it's possible to set a resource map to be readOnly, and to (sort of) resProtect individual resources, and that you can lock the file name as part of the Finder attributes, but what about the File is Locked bit?

___

There really isn't a way to lock a file on the Macintosh so that it can't be unlocked by a user with the right tools. All you can do is prevent someone from deleting or changing something by accident (they'll have to unlock the file or resource). It's kind of like locking your house or car - it prevents the casual bypasser from getting in, but not someone who really wants in.

The Finder locks or unlocks a file (via the Get Info window) with the File Manager PBHSetFLock or PBHRstFLock routines. PBHSetFLock and PBHRstFLock manipulate bit 0 (the file locked bit) in the flFlags byte of the file's directory entry. When you call the PBGetFInfo or PBGetCatInfo routines on a file, those routines return the state of the file locked bit in the ioFlAttrib field of the parameter block. The documentation for PBSetFInfo and PBSetCatInfo in Inside Macintosh Volume IV is wrong; you cannot set the attributes of a file or a folder (this is unrelated to your question, but you also cannot set a file's clump size with PBSetCatInfo). The only file attribute you can manipulate through the File Manager is the locked bit. Some bits in a directory's attributes can be manipulated when our file server software is running (see the next paragraph).

If Macintosh File Sharing or AppleShare 3.0 are running and a volume is sharable (it can be seen by the owner remotely), then the file server software adds additional privileges to the File Manager privileges. Under both versions of the file server software, you can lock folders so that they cannot be moved, renamed or deleted (see the Macintosh Tech Note "File Sharing and Shared Folders" for more information in this area) by remote users. With AppleShare 3.0, you can also "copy protect" (or unprotect) files with one of two server control calls. The copy protection only keeps the Finder from copying the file; any other copy utility can still copy the file.

You already know about resource locking, so that probably covers everything.

Allocate, AllocContig, and Macintosh file allocation

Date Written: 5/20/92

Last reviewed: 6/14/93

When we allocate space for a new file using AllocContig with an argument in multiples of clump size, we should be grabbing whole clumps at a time so that file length (and physical EOF) will be a multiple of clump size. What happens if we truncate a file by moving the logical EOF somewhere inside a clump? Inside Macintosh says disk sectors are freed at the allocation block level, so we could have a file whose physical EOF isn't a multiple of clump size, right? Does AllocContig guarantee that the new bytes added are contiguous with the end of the existing file, or only that the newly added bytes are contiguous among themselves? If the logical and physical EOFs aren't the same, does AllocContig subtract the difference before grabbing the new bytes, or do we get the extra bytes (between EOFs) as a bonus?

___

You can create a file whose physical size isn't a multiple of the clump size, if you try. When the file shrinks, the blocks are freed at the allocation level, without regard for the clump size. Therefore, if you set the logical EOF to a smaller value, you can create a file of any physical length.

There's no guarantee that the allocated bytes will be contiguous with the current end of the file. The decisions that file allocation makes are as follows:

* It always attempts to allocate contiguously, regardless of whether you're explicitly doing a contiguous allocation. (If it can't, it fails rather than proceeding if doing an AllocContig.)

* It always attempts to keep the added space contiguous with the existing space, but it will forgo this before it will fragment the current allocation request (regardless of whether you're calling Allocate or AllocContig).

So these are the actions that file allocation will take:

1. Allocate contiguous space immediately after the current physical end of file.

2. Allocate contiguous space separated from the current physical EOF.

3. Fail here if allocating contiguously.

4. Allocate fragmented space, where the first fragment follows the physical EOF.

5. Allocate fragmented space somewhere on the volume.

You don't get "extra" space with AllocContig. It just does a basic allocation but makes sure any added blocks are contiguous. PBAllocContig does not guarantee that the space requested will be allocated contiguously. Instead, it first grabs all the room remaining in the current extent, and then guarantees that the remaining space will be contiguous. For example, if you have a 1-byte file with a chunk size of 10K and you try to allocate 20K, 10K-1 bytes will be added to the current file; the remaining 10K+1 bytes are guaranteed to be contiguous.

Use OpenDF to open files with driver-like names

Date Written: 9/25/92

Last reviewed: 11/1/92

Our program has a problem with filenames that start with a period. During an Open call, if the filename starts with a period, the Open code calls the Device Manager (for drivers and DAs) instead of the File Manager. However, we've seen other applications that can successfully open these files. What's the secret? How do we open files that otherwise look (from the name) like drivers?

___

The Open trap is shared between the Device Manager and the File Manager. When Open is called, it checks first to see whether you're trying to open a driver. Driver names always start with a period. If you can, avoid using filenames that begin with a period. Macintosh Technical Note "HFS Elucidations" discusses this conflict. The secret to opening those files is using the new Open Data Fork functions available with System 7 -- FSpOpenDF, HOpenDF, and PBHOpenDF. These functions bypass the driver name check and go right to the File Manager. Here's the code we use to open a file:

err := HOpenDF(vRefNum, dirID, fileName, permission, refNum);
IF (err = paramErr) THEN      {HOpenDF call isn't available}
  err := HOpen(vRefNum, dirID, fileName, permission, refNum);
                      {try again with old HOpen call}

Try this and your problem should go away under System 7. The code retries with the regular Open call (which uses the same input parameters), so this code can be used in programs that run under both System 6 and System 7.

Should ioNamePtr point to Str255 or Str63?

Date Written: 7/15/92

Last reviewed: 9/15/92

The Macintosh Technical Note "Setting ioNamePtr in File Manager Calls" says that ioNamePtr needs to point either to nil or to storage for a Str255. This contradicts the Technical Note "Searching Volumes--Solutions and Problems," which gives an example of a recursive indexed search using PBGetCatInfo. The example uses a Str63. Which Technical Note is correct?

___

To be generically correct, ioNamePtr should point to a Str255. However, in the case of PBGetCatInfo and other calls that return a filename (or a directory name), a Str63 is sufficient. The reasons are tied to the history of the Macintosh file system.

MFS, the original Macintosh file system, supported filename lengths of up to 255 characters. However, the Finder on those systems supported filename lengths up to only 63 characters and, in fact, developers were warned to limit filename lengths to fewer than 64 characters (see page II-81 of Inside Macintosh Volume II).

HFS, the hierarchical file system (in every Macintosh ROM since the Macintosh Plus), further limited filename lengths to 31 characters. If you mount an MFS disk while running HFS, the old MFS code is called to handle the operation. So, the file system can still create and use files with long filenames on MFS volumes.

When the System 7 file system was being designed, Engineering had to decide what size string to use in FSSpec records. The decision was to use a Str63 instead of a Str31 to be able to support long MFS filenames, and to use a Str63 instead of a Str255 because there were probably very few filenames with over 63 characters (remember, the old Finder limited filenames to 63 characters). Using a Str63 instead of a Str255 saves 192 bytes per FSSpec record.

So, we recommend that you use at least a Str63 for filenames, as in "Searching Volumes--Solutions and Problems." If you need to manipulate the filename in any way after you've gotten the name--for example, to concatenate it with another string--you might want to use a Str255.

Note: Even though the System 7 file system supports filenames longer than 31 characters on MFS volumes, the System 7 Finder does not. In fact, the System 7 Finder currently crashes if you try to open an MFS volume (that is, open the volume window) that has files with names longer than 31 characters.

Macintosh file reference number (refNum) range

Date Written: 11/13/90

Last reviewed: 6/14/93

Can the refNum returned by FSOpen ever be 1? What is the range or format of legal refNums?

___

Macintosh file reference numbers (refNums) are currently positive and always fit the equation (2 + n*sizeof(FLB)) because they are offsets into the FCB table. This doesn't mean that they won't change in the future, however. To maintain system compatibility, use refNums only as they're intended to be used.

Steps for duplicating a Macintosh file

Date Written: 12/6/90

Last reviewed: 1/16/91

Is there a routine in the Macintosh operating system to duplicate a file? (Something that is similar to doing "File - Duplicate" or dragging a file from one disk to another.) We tried PBHCopyFile but, as Inside Macintosh Volume V implied, it did not work.

___

Unfortunately there isn't a routine in the Macintosh OS to duplicate a file. The only way to duplicate a file is by doing the following:

1. Create a new file,

2. Open the old file,

3. Open the new file,

4. Check to see how long the old file is,

5. Read the old file,

6. Write everything you read to the new file, then

7. Close both files.

Macintosh filename cannot start with a period (.)

Date Written: 12/18/90

Last reviewed: 5/21/90

Why do I get a bomb when I create a Macintosh filename starting with a period (.)?

___

Macintosh filenames are not allowed to begin with a period, to avoid possible confusion with driver names, which must begin with a period. (This restriction does not apply to folder names.) Ideally, the Finder should catch this possible error and require the file to be renamed, but it doesn't. Future versions of the Finder should catch this potential problem, but until then users must remember not to begin a filename with a period. See the Macintosh Technical Note "HFS Elucidations" for details.

Code for reading from a non-Macintosh-formatted floppy disk

Date Written: 6/5/91

Last reviewed: 6/14/93

My Macintosh application needs to recognize, list the files of, and read files from a DOS disk in a Macintosh application, while running under either System 6 or 7. Something akin to the way Apple File Exchange works would be fine--starting the application and then having the application recognize the DOS disk and listing its contents.

___

Assuming you want to start from scratch and write your own, instead of using a third-party software package such as DosMounter, you'll probably need to check out technical references on DOS floppy formats. Here's some code for the hard part--reading an arbitrary floppy in non-Macintosh format:

/* pass driver name, e.g. ".Sony"  */
/* returns driver Reference Number */
OSErr
Open(fn, RefNum)
StringPtr    fn;
short        *RefNum;
{
    OSErr            result;
    ParamBlockRec    pb;
   
    pb.ioParam.ioNamePtr = fn;
    pb.ioParam.ioCompletion = 0;
    pb.ioParam.ioPermssn = fsRdPerm;
    result = PBOpen(&pb, false);
    *RefNum = pb.ioParam.ioRefNum;
   
    return result;
}
/* pass refNum gotten from Open    */
/*      pointer to destination     */
/*      bytes to read              */
/*      offset from byte 0 of disk */
OSErr
Read(refNum, dest, count, offset)
short    refNum;
Ptr        dest;
long    count;
long    offset;
{
    OSErr result;
    ParamBlockRec    io;
   
    io.ioParam.ioCompletion = NULL;
    io.ioParam.ioVRefNum = 1;
    io.ioParam.ioRefNum = refNum;
    io.ioParam.ioBuffer = dest;
    io.ioParam.ioReqCount = count;
    io.ioParam.ioPosMode = fsFromStart;
    io.ioParam.ioPosOffset = offset;
    result = PBRead(&io, false);
   
    if (result != noErr)
        printf("PBRead failed. ioActCount = %d\n", io.ioParam.ioActCount);
   
    return result;
}

System 7 Finder and file duplication time

Date Written: 6/13/91

Last reviewed: 6/14/93

Duplicating a large file under the System 7 Finder took four times as long as under System 6. How can I make System 7 copy more blocks and larger sizes, to reduce duplication time?

___

Duplicating a file in the Finder is slower in System 7.0 than in previous systems because the new system allows you to switch out of the Finder to another application for those really big file duplications. This is a feature that was installed into the Finder. The Finder is constantly checking to see if it should be switched out and this takes time. Since you can't get something for nothing, even in the Macintosh world, there is a speed loss when copying files in the Finder.

This speed change will not happen from within an application. In fact, you may notice a small increase in the speed at which applications can duplicate files. Try using the "duplicate" command in MPW and you'll see that it's considerably faster than the Finder.

Copying a file or folder dragged to Macintosh application

Date Written: 6/13/91

Last reviewed: 10/8/91

What do I need to do to have my Macintosh application copy any folder or file that is dragged to it?

___

One method is to create an application and have it reside on the desktop. The user can then just drag files to that application, which will in turn receive an open event on the file and duplicate the file. This is possible because when a document's icon is dragged on top of an application's icon, the Finder will tell the application to open the file if the application knows about the file.. The only catch is that you'll need to use the special 'FREF' type that will enable the Finder to send you an 'odoc' for ANY file. The key 'FREF's you'll need are:

  **** -- any file,
  fold -- any folder,
  disk -- any disk.

The characters should be all lowercase letters. This should allow your application to receive an open for anything that's dragged to it. It will be up to your application to handle possible error conditions such as not enough disk space.

Simulating PBExchangeFiles for System 6

Date Written: 6/19/91

Last reviewed: 10/22/91

Here's how to do the equivalent of PBExchangeFiles (new in System 7) for System 6:

Create a new file. Copy the old files to the new one, do a PBHGetFileInfo on the old and and new, and copy the Finder and creation date from the old to the new. Then do a PBHSetFileInfo, renaming the new and deleting the old. The File Manager implements PBExchangeFiles as shown below:

Catmove file 1 to file 2's directory

Catmove file 2 to file 1's directory

rename file 2 to 1

rename file 1 to 2

Renaming and deleting require the filenames instead of refnums. The filename may have been changed by the user since the file was opened because the Fnder doesn't disallow changing names and moving files that are open. To get around this problem, use GetFCBInfo to recover the filename, DirID and VRefNum of an open file. In the case of files, the FCB's ioFCBParID field is the ioDirID of the file, and ioFCBVRefNum is the ioVRefNum.

You may need to do an intermediate rename if there is file already exists with the name of the moved file in the destination directory. The File Manager is able to pull this procedure off a little more gracefully because it manipulates the B-Tree entries instead of going through the APIs.

Macintosh open file maximums & how to alter

Date Written: 7/11/91

Last reviewed: 6/14/93

There's a limit on the maximum number of files that you can have open on the Macintosh at one time. This varies between System 7 and earlier versions of the Macintosh operating system.

System 7 is basically limited in this area only by the amount of RAM that you have available. It dynamically allocates more memory for the file control blocks, depending on the number of files that you desire to open. So this typically is not a problem under System 7.

System 6, however, defaults to 40 open files at once. This is most easily altered by the use of an init called "Up Your FCBs," available on the current Developer CD Series disc. Please note that you shouldn't try to use this INIT with System 7; it isn't necessary and most likely will cause you problems. Alternatively, you can change this value yourself by modifying your SystemStartup boot block information. This is fairly easy to do, but it should be noted that Apple doesn't support modifications to your boot blocks.

The SystemStartup information (for a Macintosh system or a file server), is stored in logical blocks 0 and 1 of an HFS startup disk. It contains the maximum number of files that can be open simultaneously, with a default of 10. The location for this parameter is $7A of logical block 0 on an HFS disk. The number at $7A (default is $000A or decimal 10) serves as the parameter for files open at one time.

The number $000A (or decimal 10), for instance, is proper for 128K and 512K systems. If the Macintosh is equipped with 1 MB or more of memory, the system multiplies the number at $7A by four (that is, it becomes decimal 40) and uses the new number as the new file limiter.

You can use a disk editor, like FEdit, to change this number. The file control block buffer and system heap size will be calculated with the changed file's open parameter upon restarting. If you must raise this number, do it in small increments. Raising the number in location $7A, as some users have done, can force the system to set aside too much memory. This causes a system error, which, because it is unexpected, the system cannot handle. The result is a system lockup.

The MPW linker tends to stretch the open file maximum. If you're running into problems trying to link too many files at once, you might consider using the lib utility to combine some of these files, thus requiring fewer file control blocks to be open at once during the link process.

X-Ref:

Macintosh Technical Note "Boot Blocks"

Where to get Macintosh third-party file formats

Date Written: 8/30/91

Last reviewed: 8/30/91

How can I get the file format details on the likes of MacWrite, MacWrite II, and Microsoft Word?

___

Macintosh file formats are not published like Apple II file formats. You must contact the developers of the Macintosh software in order to obtain their file formats.

FSSpec and SFReply information blocks

Date Written: 8/19/91

Last reviewed: 10/8/91

How can I make FSSpec file information comply with what was an SFReply information block? Is there a way to convert FSSpec information--as passed, for example, via an Open Documents Apple event--to a vRefNum as understood by an SFReply record? We want to keep our tried-and-true non-System 7 file management logic and convert from FSSpec to SFReply-type format.

___

Not wanting to make a good bit of file system code obsolete is understandable; however, while it's unlikely that Apple will dispense with support for old SFGetFile or SFPutFile functions in the near future, the use of SFReply-style data structures in internal calls has no development future.

The vRefNum field of the SFReply record was originally (in Inside Macintosh Volume II days) a volume reference number; later, with the creation of HFS in 1986, it became a working directory reference number for purposes of backward compatibility. In HFS, a file or directory entity on a volume is specified with a volume reference number, a directory ID, and a name. An FSSpec contains this latter information.

Converting from FSSpec to SFReply requires that your application manage the manipulation of working directory entities, which has disadvantages from the point of view of the system and compatibility. There are several difficulties with working directory references:

* There's a system-wide limit on their number.

* If you have a working directory reference to which no file buffers are open and some other application closes that working directory without your knowledge of it, your internally stored reference number is invalid and you have no way of knowing about it.

* The documentation about where, when, and how to close a working directory is somewhat ambiguous.

* An FSSpec can refer to either a file or a directory while an SFReply can refer only to a file.

Developer Technical Support urges you to take the time to remove dependencies on SFReply data structures as soon as is feasible.

How to search only nonserver mounted volumes

Date Written: 8/29/91

Last reviewed: 6/14/93

If I don't want to search any AppleShare or File Sharing volumes, how can I tell which mounted volume to search?

___

You should be able to use the field vMServerAdr in the GetVolParmsInfo attributes buffer of PBHGetVolParms to determine whether to search a volume. Since the vMServerAdr field specifies the internet address of the server that manages an AppleTalk server volume, checking for a zero internet address before searching the volume would seem the way to go for you.

X-Ref:

Inside Macintosh Volume VI, pages 25-37 to 25-40

Using the Macintosh file system asynchronously at interrupt time

Date Written: 11/19/91

Last reviewed: 6/14/93

Is it true that calls in the file system like PBOpen can move memory under all conditions? Can I create, open, write, and close a file completely at interrupt time? If not, which calls must be called at a driver's accRun time? I need to be compatible with both 6.0.x and 7.0.x.

___

The answer to your question is (drum roll...) 42. Oops, wrong question.

The answer to your question is yes, all this (and more) can be done completely at interrupt time. Any call that can be made asynchronously can be safely made at interrupt time, provided it is made asynchronously. Glancing though Inside Macintosh Volume IV, this includes just about all of the File Manager, except for the ability to mount and unmount volumes.

One caveat: making a call asynchronously here means really making it asynchronously; making the call and then sitting in a little loop waiting for the ioResult field to change does not qualify. You must either use completion routines to determine when a call has completed, or you must check the ioResult from time to time, never waiting for it at interrupt time. (And in this case, a deferred task does qualify as being at interrupt time).

Using Macintosh PBRead call asynchronously

Date Written: 11/26/91

Last reviewed: 2/18/92

Can PBRead be called asynchronously? I would like to start the "next" file read while processing the "previous" read's data.

___

You can certainly call PBRead asynchronously; however, the results you get may be a little unexpected: If the device you're reading from is accessed via the SCSI Manager, in certain respects the effect will be synchronous. Our SCSI Manager does not currently support asynchronous operations, so once you actually make a SCSI Manager call your application won't get control again until that operation has completed. It sounds from your question like you want to be able to do processing while the read is being serviced, which unfortunately won't be possible unless Apple does an asynchronous SCSI Manager.

The question that instantly pops to mind is "What good is an asynchronous call anyway?" The important thing that you get is the ability to make a read request at interrupt time and have it serviced when the driver is next available. You'll still lose the PC while the read is being serviced, but it will be invisible to your application.

Partition Macintosh volumes to work around 2 GB size limit

Date Written: 11/26/91

Last reviewed: 6/14/93

How do I create a Macintosh volume that is >2 gigabytes in size? Up to 2 GB everything works OK. Since my driver and the File Manager work with disk/allocation blocks, 2 GB falls well within a longint. Is this limitation imposed by the Finder?

___

There is currently a 2 GB ceiling on the size of a volume. This is due to a system software "feature" where the volume size field is declared as a "long" as opposed to an "unsigned long," clipping 4 GB off your possible storage capacity. The real bummer here is that there is no workaround, so until someone changes portions of the Finder and file system, the 2 GB limit will remain.

This is a wonderful application for multiple partitions, however. Basically the only way around this limitation is to put lots of smaller partitions on your disk. By partitioning the volume into smaller chunks you'll also be able to use a smaller Allocation Block size. This means that you'll waste less space on your disk.

The problem with multiple partitioning is that it is a little tricky: You're going to have to write a driver that searches the partition map at the beginning of the disk, creating drive queue elements and issuing Disk Inserted events for each of them. It would also be nice if you provided a utility for your users that will allow them to remount a volume on the disk once it's been unmounted, since the only way to do that otherwise would be to restart and have the driver do it as part of its initialization procedure. Things get really unpleasant with removable media. The questions you'll need to ask yourself as you're designing the driver are When can I eject this thing? What does being offline really mean in my case? and How do I handle disk switch dialogs? to name a few. DTS doesn't really have answers to these questions, so we've stamped this Unsupported.

Accessing files in a folder dropped onto an application

Date Written: 12/5/91

Last reviewed: 6/14/93

How can I get to the files in a folder which was dropped onto my System 7 application? PBGetCatInfo doesn't work with the dirID from the FSSpec.

___

To determine if the FSSpec returned by AEGetNthPtr points to a folder (and if so, to find its dirID), call PBGetCatInfo and check the ioFlAttrib field of the CInfoPBRec after the call. If bit 4 is set, the item is a folder.

Remember that the FSSpec for a folder indicates its parent's dirID, not its own. Before the call to PBGetCatInfo, the ioDrDirID field of the CInfoPBRec should contain the folder's parent's dirID, ioNamePtr must point to the name of the folder, and ioFDirIndex must be zero. Also assign the ioVRefNum and ioCompletion fields appropriately. When PBGetCatInfo returns, ioDrDirID will contain the folder's own dirID.

Use the folder's own dirID in calls to PBHGetFInfo (or PBGetCatInfo) with an increasing index parameter to identify all of the files (or files and directories) contained in the folder.

A sample function showing how to do this is pasted below. PBGetCatInfo is documented in the File Manager chapter of Inside Macintosh Volume IV, and in the Macintosh Technical Note "Setting ioFDirIndex in PBGetCatInfo Calls." An application must also have a 'fold' FREF resource included in its bundle to allow folders to be dropped onto it, as discussed in the Finder Interface chapter of Inside Macintosh Volume VI.

FUNCTION DoAEOpenDoc(theAEvent: AppleEvent; reply: AppleEvent; 
                     refcon: LONGINT): OSErr;
 { handle each item in each folder opened (only one folder deep - not 
   recursive) }
  VAR
   retCode: OSErr;
   docList: AEDescList;
   odocFSSpec: FSSpec;
   index, itemsInList: LONGINT;
   actualSize: Size;
   keywd: AEKeyword;
   retdType: DescType;

   fileName: Str255;
   folderContentsFSSpec: FSSpec;
   folderDirID: LONGINT;
   folderIndex: INTEGER;

   myCInfoPBRec: CInfoPBRec;

  BEGIN

   retCode := AEGetParamDesc (theAEvent, keyDirectObject, 
                              typeAEList, docList);
   IF retCode <> noErr THEN
    BEGIN
     { never ExitToShell from within an AE handler }
     PostWarning('cannot get parameter descriptor', retCode);
     DoAEOpenDoc := retCode;
     EXIT(DoAEOpenDoc)
    END;

   retCode := AECountItems(docList, itemsInList);

   FOR index := 1 TO itemsInList DO
    BEGIN
     retCode := AEGetNthPtr(docList, index, typeFSS, keywd, 
                            retdType, @odocFSSpec,
      sizeof(odocFSSpec), actualSize);
     IF retCode <> noErr THEN
      BEGIN
       PostWarning('cannot get nth document', retCode);
       DoAEOpenDoc := retCode;
       EXIT(DoAEOpenDoc)
      END;

     fileName := odocFSSpec.name;

     myCInfoPBRec.ioCompletion := NIL;
     myCInfoPBRec.ioNamePtr := @fileName;
     myCInfoPBRec.ioVRefNum := odocFSSpec.vRefNum;
     myCInfoPBRec.ioDrDirID := odocFSSpec.parID;
     myCInfoPBRec.ioFDirIndex := 0; { use name and dirID }
     retCode := PBGetCatInfoSync(@myCInfoPBRec);
     IF retCode <> noErr THEN
      BEGIN
       PostWarning('cannot get cat info', retCode);
       DoAEOpenDoc := retCode;
       EXIT(DoAEOpenDoc)
      END;

     IF BTST(myCInfoPBRec.ioFlAttrib, 4) THEN { it's a directory }
      BEGIN
       { index through all items in the directory }

       folderIndex := 0;

       { myCInfoPBRec.ioDrDirID now contains the dirID of the folder
        pointed to by odocFSSpec }
       folderDirID := myCInfoPBRec.ioDrDirID;
       REPEAT
        folderIndex := folderIndex + 1;
        fileName := ''; { reset name string }
       
        myCInfoPBRec.ioCompletion := NIL;
        myCInfoPBRec.ioNamePtr := @fileName;
        myCInfoPBRec.ioVRefNum := odocFSSpec.vRefNum;
        myCInfoPBRec.ioDrDirID := folderDirID;
        myCInfoPBRec.ioFDirIndex := folderIndex;
        retCode := PBGetCatInfoSync(@myCInfoPBRec);
        IF retCode = noErr THEN
        BEGIN
         retCode := FSMakeFSSpec(odocFSSpec.vRefNum, folderDirID, fileName,
          folderContentsFSSpec);
         IF retCode <> noErr THEN
          BEGIN
           PostWarning('cannot make FSSpec', retCode);
           DoAEOpenDoc := retCode;
           EXIT(DoAEOpenDoc)
          END;

         { now do something with the item }
         DoSomethingWithTheItem(folderContentsFSSpec);
        END;

       UNTIL retCode <> noErr; { exhausted folder contents }
      END

     ELSE { odoc entry for a file, not for a folder }
      DoSomethingWithTheItem(odocFSSpec);

    END; { for all items in docList }

   retCode := AEDisposeDesc(docList);

   DoAEOpenDoc := noErr;
  END; { DoAEOpenDoc }

Ignore asynchronous low-level File Manager function results

Date Written: 2/18/92

Last reviewed: 6/14/93

I'm making an asynchronous low-level File Manager call from inside a completion routine (for example, "error := PBxxx(@PB, TRUE);"). Occasionally on some machines, the call immediately returns an error in the function result even though everything appears to work correctly. Do I need to worry about the function result when I make the call?

___

It sounds like you're making the mistake of testing the function result of an asynchronous File Manager call (the value of register D0 is returned in the function result). There is no useful information in the function result of an asynchronous call made to the File Manager; the call might not even have been looked at by the File Manager yet. It's only ioResult after the call completes, or either DO or ioResult at the entry to the completion routine that contains the call's result status. If you're polling to check for the call's completion, ioResult will indicate the call has completed when it is less than or equal to 0.

In general, when making asynchronous I/O calls (reads or writes) there are only two types of function result errors that are of any possible consequence: a driver not open error (notOpenErr) or a driver reference number error (badUnitErr or unitEmptyErr), which indicate the call was not successfully enqueued by the driver and the ioCompletion routine will not be called. Neither one of these error conditions makes any sense for the File Manager (which isn't a driver); the File Manager will always call the completion routine (if any) of a given asynchronous call.

Your program should just always ignore the function result of an asynchronous low-level File Manager call and leave it up to the completion routine or the routine polling ioResult to check for and handle any errors that may have happened on the call.

How to get Macintosh file label & color strings

Date Written: 3/2/92

Last reviewed: 4/7/92

We'd like to search for files by label or color, getting the actual string for the label/color field, so that the user can select from a menu that looks like what they'd see in the Finder or ResEdit.

___

In the Icon Utilities package is a call that will get you the RGB color and string for the Finder's labels. Information from the Macintosh Tech Note "Drawing Icons the System 7 Way" draft is pasted below. It includes the glue code for the call in MPW C format.

Function GetLabelColor(labelNumber:Integer; var labelColor:RGBColor;
                       VAR LabelString:str255):OSErr;
INLINE $303C, $050B, ABC9;

This call returns the actual color and string used in the label menu of the Finder and the label's Control Panel. This information is provided in case you wish to include the label text or color when displaying a file's icon in your application.

In C the call looks like:

pascal OSErr GetLabelColor(short labelNumber,RGBColor *labelColor,
                           Str255 labelString)={0x303C, 0x050B, 0xABC9};

The assembly being:
;         Push the usual stuff
          Move.W  #$050B,D0
          _IconDispatch                        ; $ABC9

You can use this call to get both the string and the color.

X-Ref: Macintosh Technical Note "Drawing Icons the System 7 Way"

_CatMove vs _HReName

Date Written: 11/30/90

Last reviewed: 6/14/93

When using the Macintosh _CatMove trap, can I pass the ioNewName field and leave the ioNewDirID field nil? When tracing through a few calls to this trap, it seems that _CatMove works just fine when the reverse is true (ioNewName is nil).

___

It sounds like you are trying to get _CatMove to behave like _HRename. Leaving the ioNewName field nil is reasonable if you are just changing the position of the file in the directory structure without affecting its name. In the case of filling in ioNewName and leaving ioNewDirID blank (if it were allowed), this would be like saying "change the name and leave the directory unchanged." This is definitely a job for _HRename.

Macintosh verified read error produces dataVerErr (-68)

Date Written: 11/30/90

Last reviewed: 6/14/93

If I call _Read with "44-ioPosMode" ORed with $40 for a Macintosh verify, what error code is returned if the verify is not successful?

___

An error in a verified read should produce an dataVerErr (-68).

Determining the amount of free space on a Macintosh disk

Date Written: 11/17/89

Last reviewed: 3/8/91

How can I determine the amount of free space on my Macintosh disk?

___

This is determined from information obtained from a pbHGetVInfo call. Determine the free space by multiplying the number of free blocks on the disk by the size of each of these blocks. Here's an example in MPW Pascal:

TwoIntsMakesALong = RECORD
{ Necessary trick to take into account that ioVFrBlk is unsigned }
    CASE Integer OF
        1: (long: LongInt);
        2: (ints: ARRAY [0..1] OF Integer);
    END; {TwoIntsMakesALong}

VAR
    myHPB          : HParamBlockRec;
    convert        : TwoIntsMakesALong;

BEGIN
    WITH myHPB DO BEGIN         { set up the block for the PBHGetVInfo call}
        ioNamePtr := NIL;       { we don't care about the name}
        ioVRefNum := yourVRefNum;
        ioVolIndex := 0;        { use ioVRefNum only}
    END; {with}
    err := PBHGetVInfo(@myHPB,FALSE);

    convert.ints[0] := 0;
    convert.ints[1] := myHPB.ioVFrBlk;
    FreeSpace := convert.long * myHPB.ioVAlBlkSiz;
END;

In MPW C, it looks something like this:

HParamBlockRec  myHPB;
longint         FreeSpace

myHPB.volumeParam.ioNamePtr = nil;       /* don't care about the name */
myHPB.volumeParam.ioVRefNum = yourVRefNum;
myHPB.volumeParam.ioVolIndex = 0;       /* use ioVRefNum only */

err = PBHGetVInfo(&myHPB,false);

FreeSpace = (long)myHPB.volumeParam.ioVFrBlk * myHPB.volumeParam.ioVAlBlkSiz;

X-Refs:

"The File Manager," Inside Macintosh Volumes I, II, IV, and VI

Macintosh Technical Note "Problem with GVInfo"

How to determine if a Mac file or resource file is already open

Date Written: 5/3/89

Last reviewed: 6/14/93

How can I tell if a Macintosh file or a resource file is already open?

___

Use the File Manager routines PBGetFInfo (IM IV: 148), PBHGetFInfo (IM IV:149), and PBGetCatInfo (IM IV: 155) to determine if a file is open, and which forks (resource and data) of the file are open. All these routines return ioFlAttrib in the parameter block, which has bits set or cleared to indicate if the file is locked, open, or a directory, and which forks are open (IM IV:125).

OpenResFile will then call the File Manager to open the resource fork of a file. The refNum returned by OpenResFile is a File Manager reference number. To find out if a resource file is already open (so you don't accidently close it, for example), just check if the resource fork of the file is open (before you call OpenResFile). If it is, call OpenResFile to get the refNum, but don't call CloseResFile.

Here's a fragment in C:

  /* uses a full pathname rather than vRefNum/dirId  */

  HFileInfo  fParams;
  ...
  fParams.ioCompletion = NIL;
  fParams.ioVRefNum = 0;
  fParams.ioFDirIndex = 0;
  fParams.ioDirID = 0L;
  fParams.ioNamePtr := "\pVolume:Folder:Filename";  /* Pascal string */
  err = PBGetCatInfo(fParams, FALSE);
  if (fParams.ioFlAttrib & 0x10)
    /* pathname is a directory */
  else if (fParams.ioFlAttrib & 0x04)
    /* resource fork is open */
  else ...

X-Refs:

"The File Manager," Inside Macintosh Volumes I, II, IV, and VI

Macintosh Technical Note "New High-Level File Manager Calls"

How to get the correct size of a Macintosh file

Date Written: 12/13/90

Last reviewed: 6/14/93

How do I determine the size of a Macintosh file? I want to get exactly what the Finder reports when you do Get Info on a selected file. How do I use the FileParam fields, ioFlLgLen, ioFlPyLen, ioFlRLgLen, and ioFlRPyLen to find out the size of a disk file after a call to PBGetFInfo?

___

To get the correct size of a Macintosh file one needs to sum the physical sizes of the resource and data forks. So, you'll need to add the fields ioFlPyLen and ioFlRPylen returned in the parameter block.

Retained Macintosh file reference requirements

Date Written: 4/8/91

Last reviewed: 6/21/91

How do I retain a persistent reference to a file? What can I store in a configuration file so I can find a specific file again at a later launch?

___

The Macintosh has two native file systems: the original (64K ROM) Macintosh File System (MFS) and the Hierarchical File System (HFS) (Macintosh Plus and newer). In an attempt to simplify this discussion, I'll assume you know the differences between them and base my explanation upon the HFS system as it is the current one.

To retain a reference to a file location that will persist across application launch and restarts, store its file name, volume name, and directory ID. With this information, you will be able to obtain the volume reference number (a dynamic value based on mount order, subject to change because the addition of another drive can change the mount order) and then use either the new high-level File Manager calls or the parameter block-based calls to operate on these files. If you happen to encounter an MFS volume, the HFS File Manager will do the appropriate thing given that you use the calls correctly.

Only if you are supporting MFS for an old system will you need to call the MFS functions for obtaining the correct reference values; MFS was a flat-file system.

Standard File's reply.vRefNum value when used with HFS is actually a working directory reference number, not a fixed reference; therefore it will usually change between launches of the application or across restarts.

Documentation for the File Manager is extensive and spread out because it was updated when the Macintosh Plus was released and has been added to subsequently by Technical Notes. Inside Macintosh Volume II deals with the MFS File Manager, Inside Macintosh Volumes IV and V deal with the HFS File Manager through System 6.0.x, and Inside Macintosh Volume VI (on your System 7.0 CD-ROM) lists the new File Manager services available under System 7.0. The following Technical Notes should prove useful to you:

File Manager:

"Available Volumes"

"Determining Which File System is Active"

"HFS Ruminations"

"HFS Elucidations"

"Why PBHSetVol is Dangerous"

"Problem with GetVInfo"

"Setting ioNamePtr in File Manager Calls"

"Working Directories and MultiFinder"

"HFS Tidbits"

"New High-Level File Manager Calls"

"Mixing HFS and C File I/O"

Standard File:

"Customizing Standard File"

"Standard File Tips"

PBHCopyFile and Macintosh file copying

Date Written: 3/18/91

Last reviewed: 7/29/91

I was just about to write a Macintosh file copying function when I decided to look up a definition in HFS.h (THINK C 4.0.2) and stumbled across the following definition:

    typedef struct {
       STANDARD_PBHEADER
       int ioDstVRefNum;
       int filler8;
       Ptr ioNewName;
       Ptr ioCopyName;
       longioNewDirID;
       longfiller14;
       longfiller15;
       longioDirID;
    } CopyParam;

Isn't this a PB for a file copying function?

___

The ParamBlock structure that you found is indeed for the PBHCopyFile call. PBHCopyFile is documented in Inside Macintosh Volume V, the File Manager chapter. The hitch is that it is an optional call for AppleShare file servers, and it works only intraserver (but can work across volumes that are on the same server). You can determine whether a particular volume supports PBHCopyFile by calling PBHGetVolParms on that volume and checking the bHasCopyFile flag (bit 14) of the VMAttrib field. Also, you can determine whether two volumes are on the same server by calling PBHGetVolParms on them and comparing their internet addresses. If they are the same, then they are on the same server.

Unfortunately, if what you are looking for is a single call to copy a file on the Macintosh, there isn't one (oddly enough). To handle the general case of copying a file on the Macintosh, you still have to copy the file's data fork, resource fork, and Finder Info flags, except for the INITed bit.

Copying a file from application folder to System Folder

Date Written: 3/11/91

Last reviewed: 7/25/91

My installer application should automatically copy known files from either a floppy or a folder in the same directory or folder as the application. How can I determine a reference number for the source file in the directory without using the Standard File Package SFGetFile?

___

Regarding your question as to how best to go about copying some files from the same folder as your application to the System Folder, here are a few thoughts:

For any File Manager call that expects only a vRefNum--that is, it isn't one of the HFS-specific calls--passing a vRefNum of zero (0) will refer to the same folder as the application. So, for example, you could pass zero to PBOpen to read the data fork of the file, and you could pass zero to read the resource fork. Note that in both cases, you should pass the name of the file as well, and specify read permission only, since your intent is to copy the file, not to write to it.

You should use the PB calls because they allow you to specify access privileges to the files, which is a particularly good idea in a shared environment, such as MultiFinder or a file server. If you do use the PB calls to open the file forks, you should also use them for reading and writing, as mixing and matching high- and low-level File Manager calls tends to give the operating system indigestion, to say nothing of what it would do to the programmer.

Don't forget to get the File Info from the source file and set the File Info of the copy! But in the process, don't forget not to copy the INITed bit.

So, in summary: Use a vRefNum of zero to refer to the same folder as the application, refer to the files by name, and watch out for the above points, and everything should go smoothly.

Macintosh Read calls at interrupt time

Date Written: 9/26/91

Last reviewed: 6/14/93

Read calls at interrupt time often result in a "hang," waiting for the parameter block to show "done." This happens if the interrupt occurred during another Read call. I've tried checking the low-memory global FSBusy, and that decreases the occurrence of this problem but does not eliminate it. When is it safe to make the Read call?

___

The problem you're experiencing is a common one known as "deadlock." The good news is that you can always make Read calls at interrupt time! The only requirement is that you make them asynchronously and provide a completion routine, rather than loop, waiting for the ioResult field to indicate the call has completed. This will require that you use the lower-level PBRead call, rather than the high-level FSRead.

The low-memory global FSBusy is not a reliable indicator of the state of the File Manager. The File Manager's implementation has changed over time, and new entities patch it and use the hooks it offers to do strange and wonderful things. File Sharing really turns it on its ear. The result is that when FSBusy is set, you can be sure the File Manager is busy, but when it's clear you can't be sure it's free. Therefore, it would be best if you ignore its existence.

If you need to have the Read calls execute in a particular order, you'll have to chain them through their completion routine. The basic concept is that the completion routine for the first Read request initiates the next Read request, and so on until you're done reading.

By the way, never make synchronous calls at interrupt time (and, contrary to the popular misconception, deferred tasks are still considered to be run at interrupt time) or from ioCompletion routines, which may get called at interrupt time.

Should ioNamePtr point to Str255 or Str63?

Date Written: 7/15/92

Last reviewed: 6/14/93

The Macintosh Technical Note "Setting ioNamePtr in File Manager Calls" says that ioNamePtr needs to point either to nil or to storage for a Str255. This contradicts the Technical Note "Searching Volumes--Solutions and Problems," which gives an example of a recursive indexed search using PBGetCatInfo. The example uses a Str63. Which Technical Note is correct?

___

To be generically correct, ioNamePtr should point to a Str255. However, in the case of PBGetCatInfo and other calls that return a filename (or a directory name), a Str63 is sufficient. The reasons are tied to the history of the Macintosh file system.

MFS, the original Macintosh file system, supported filename lengths of up to 255 characters. However, the Finder on those systems supported filename lengths up to only 63 characters and, in fact, developers were warned to limit filename lengths to fewer than 64 characters (see page II-81 of Inside Macintosh Volume II).

HFS, the hierarchical file system (in every Macintosh ROM since the Macintosh Plus), further limited filename lengths to 31 characters. If you mount an MFS disk while running HFS, the old MFS code is called to handle the operation. So, the file system can still create and use files with long filenames on MFS volumes.

When the System 7 file system was being designed, engineering had to decide what size string to use in FSSpec records. The decision was to use a Str63 instead of a Str31 to be able to support long MFS filenames, and to use a Str63 instead of a Str255 because there were probably very few filenames with over 63 characters (remember, the old Finder limited filenames to 63 characters). Using a Str63 instead of a Str255 saves 192 bytes per FSSpec record.

So, we recommend that you use at least a Str63 for filenames, as in "Searching Volumes--Solutions and Problems." If you need to manipulate the filename in any way after you've gotten the name--for example, to concatenate it with another string--you might want to use a Str255.

Note: Even though the System 7 file system supports filenames longer than 31 characters on MFS volumes, the System 7 Finder does not. In fact, the System 7 Finder currently crashes if you try to open an MFS volume (that is, open the volume window) that has files with names longer than 31 characters.



Tech Support
Technotes
Previous Technote | Contents | Next Technote


Navigation graphic, see text links

Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help